﻿using Opc.Ua;
using OpcUaHelper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace autoCheck
{
    public class OperatorOPCUa
    {
        public ExpandedNodeId TypeDefinition { get; set; }
        private OpcUaClient opcUaClient = new OpcUaClient();

        /// <summary>
        /// 连接OPC ua Server 
        /// </summary>
        /// <param name="serverUrl">服务地址</param>
        public bool ConnectOpcUa(string serverUrl)
        {
            try
            {
                if (opcUaClient.Connected == true)
                {
                    return true;
                }
                else
                {
                    opcUaClient.ConnectServer(serverUrl);
                    return false;
                }

            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 关闭连接OPC ua Server 
        /// </summary>
        public void CloseOpcUa()
        {
            opcUaClient.Disconnect();
        }
        public bool ReadItemValue(string dbNumber, string itemLabel, ref string itemValue)
        {
            try
            {
                string sendCmd = "ns=" + dbNumber + ";s=" + itemLabel;
                Opc.Ua.DataValue value = opcUaClient.ReadNode(sendCmd);
                itemValue = SetDataValueToString(value);
                return true;
            }
            catch (Exception)
            {
                itemValue = "";
                return false;
            }
        }

        /// <summary>
        /// 获取点最小周期
        /// </summary>
        /// <param name="sendCmd">点命令信息</param>
        /// <param name="miniTime">最小时间周期</param>
        /// <returns></returns>
        public bool GetPropertyMiniTime(string sendCmd, ref int miniTime)
        {
            OpcNodeAttribute[] nodeAttributes = opcUaClient.ReadNoteAttributes(sendCmd);
            foreach (var item in nodeAttributes)
            {
                if (item.Name == "MinimumSamplingInterval")
                {
                    string result = System.Text.RegularExpressions.Regex.Replace(item.Value.ToString(), @"[^0-9]+", "");
                    miniTime = int.Parse(result);
                    if (result != "")
                    {
                        miniTime = int.Parse(result);
                        //当时钟周期未获取到则设置为200毫秒，如果获取到时钟周期则设置其周期+100毫秒
                        if (miniTime < 100)
                        {
                            miniTime = 100;
                        }
                    }
                    else
                    {
                        miniTime = 200;
                    }
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 获取节点的属性描述信息
        /// </summary>
        /// <param name="sendCmd"></param>
        /// <returns></returns>
        public bool GetPropertyDiscription(string sendCmd, ref string discription)
        {
            OpcNodeAttribute[] nodeAttributes = opcUaClient.ReadNoteAttributes(sendCmd);
            int count = 0;
            foreach (var item in nodeAttributes)
            {
                if (item.Name == "Description")
                {
                    count++;
                    discription = item.Value.ToString();
                }
            }
            return false;
        }

        /// <summary>
        /// 读取标签值，如果标签不存在则返回false
        /// </summary>
        /// <param name="sendCmd">读取命令</param>
        /// <param name="itemValue">返回值</param>
        /// <returns></returns>
        public bool ReadItemValue(string sendCmd, ref string itemValue)
        {
            //try
            //{
            itemValue = SetDataValueToString(opcUaClient.ReadNode(sendCmd));
            //}
            //catch
            //{
            //    return false;
            //}
            return true;
        }

        /// <summary>
        /// 读取标签值，如果标签不存在则返回false
        /// </summary>
        /// <param name="sendCmd">读取命令数组</param>
        /// <param name="itemValue">返回值</param>
        /// <returns></returns>
        public bool ReadItemValue(string[] sendCmd, ref string itemValue)
        {
            try
            {
                int valueCount = sendCmd.Count();
                switch (valueCount)//最大为三位
                {
                    case 1://表示1个FEP点控制1个OBJ点值
                        itemValue = SetDataValueToString(opcUaClient.ReadNode(sendCmd[0]));
                        break;
                    case 2://表示2个FEP点控制1个OBJ点值
                        itemValue = (int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[0])))
                            + int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[1]))) * 2).ToString();
                        break;
                    case 3://表示3个FEP点控制1个OBJ点值
                        itemValue = (int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[0])))
                            + int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[1]))) * 2
                            + int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[2]))) * 4).ToString();
                        break;
                    default:
                        break;
                }
                return true;
            }
            catch (Exception)
            {
                itemValue = "";
                try
                {
                    int valueCount = sendCmd.Count();
                    switch (valueCount)//最大为三位
                    {
                        case 1://表示1个FEP点控制1个OBJ点值
                            itemValue = SetDataValueToString(opcUaClient.ReadNode(sendCmd[0].Replace(".In_Channel0", "")));
                            break;
                        case 2://表示2个FEP点控制1个OBJ点值
                            itemValue = (int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[0].Replace(".In_Channel0", ""))))
                                + int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[1].Replace(".In_Channel1", "")))) * 2).ToString();
                            break;
                        case 3://表示3个FEP点控制1个OBJ点值
                            itemValue = (int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[0].Replace(".In_Channel0", ""))))
                                + int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[1].Replace(".In_Channel1", "")))) * 2
                                + int.Parse(SetDataValueToString(opcUaClient.ReadNode(sendCmd[2].Replace(".In_Channel2", "")))) * 4).ToString();
                            break;
                        default:
                            break;
                    }
                    return true;
                }
                catch
                {
                }
                return false;
            }
        }

        /// <summary>
        /// 设置节点下的所有设备为使能状态
        /// </summary>
        /// <param name="rootNodeCmd">节点名称编号命令</param>
        /// <returns></returns>
        public bool SetAllDeviceEnable(string rootNodeCmd)
        {
            string str = "";
            try
            {
                Opc.Ua.ReferenceDescription[] reference = opcUaClient.BrowseNodeReference(rootNodeCmd);
                foreach (var refer in reference)
                {
                    CallMethod(refer.NodeId.ToString(), 5, "ns=9;i=232");
                    //CallMethod(refer.NodeId.ToString(), 5, "SendCmd");
                    str += refer.NodeId.ToString() + "\r\n";
                }
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 设置节点下的所有设备为使能状态
        /// </summary>
        /// <param name="rootNodeCmd">节点名称编号命令</param>
        /// <returns></returns>
        public Dictionary<string, List<string>> GetAllDeviceInfo(string rootNodeName, string rootID, string DBNumber)
        {
            Dictionary<string, List<string>> itemInfo = new Dictionary<string, List<string>>();
            try
            {
                //获取所有设备名称
                string discriptonCmd = "ns=" + rootID + ";s=" + rootNodeName;
                Opc.Ua.ReferenceDescription[] refeRoot = opcUaClient.BrowseNodeReference(discriptonCmd);
                foreach (var deviceName in refeRoot)
                {
                    try
                    {
                        //获取所有设备的描述
                        string propertyCmd = "ns=" + rootID + ";s=" + deviceName.DisplayName.ToString() + "." + deviceName.DisplayName.ToString();
                        Opc.Ua.ReferenceDescription[] refeProperty = opcUaClient.BrowseNodeReference(propertyCmd);
                        foreach (var property in refeProperty)
                        {
                            try
                            {
                                string sttributeCmd = "ns=9002;s=" + property.DisplayName.ToString().Insert(property.DisplayName.ToString().Length - 4, ".");
                                OpcNodeAttribute[] nodeAttributes = opcUaClient.ReadNoteAttributes(sttributeCmd);
                                foreach (var attr in nodeAttributes)
                                {
                                    if (attr.Name == "Description")
                                    {
                                    }
                                }
                            }
                            catch
                            {
                                continue;
                            }
                        }
                    }
                    catch
                    {
                        continue;
                    }
                }
                return itemInfo;
            }
            catch (Exception)
            {
                return itemInfo;
            }
        }

        /// <summary>
        /// 设置节点下的所有设备为使能状态
        /// </summary>
        /// <param name="rootNodeCmd">节点名称编号命令</param>
        /// <returns></returns>
        public Dictionary<string, string> GetAllDeviceNameAndDesp(string rootNodeCmd)
        {
            Dictionary<string, string> nameAndDesp = new Dictionary<string, string>();
            try
            {
                Opc.Ua.ReferenceDescription[] reference = opcUaClient.BrowseNodeReference(rootNodeCmd);
                foreach (var refer in reference)
                {
                    nameAndDesp.Add(refer.NodeId.ToString(), refer.DisplayName.ToString());
                }
                return nameAndDesp;
            }
            catch (Exception)
            {
                return nameAndDesp;
            }
        }

        /// <summary>
        /// 调用call函数
        /// </summary>
        /// <param name="sendCmd">调用命令</param>
        /// <param name="nodeNumber">节点数据</param>
        /// <param name="nodeString">节点名称</param>
        /// <returns></returns>
        public bool CallMethod(string sendCmd, object nodeNumber, string nodeString)
        {
            try
            {
                if (nodeNumber.GetType() == typeof(int))
                {
                    if ((int)nodeNumber == 5)
                    {
                        opcUaClient.CallMethodByNodeId(sendCmd, nodeString, nodeNumber, "04")[0].ToString();
                    }
                    else if ((int)nodeNumber == 6)
                    {
                        opcUaClient.CallMethodByNodeId(sendCmd, nodeString, nodeNumber, "0")[0].ToString();
                    }
                }
                else if (nodeNumber.GetType() == typeof(bool) || nodeNumber.GetType() == typeof(Boolean))
                {
                    opcUaClient.CallMethodByNodeId(sendCmd, nodeString, true, nodeNumber);
                }
                else if (nodeNumber.GetType() == typeof(string))
                {
                    if (nodeNumber.ToString() == "取消强制")
                    {
                        opcUaClient.CallMethodByNodeId(sendCmd, nodeString, false, nodeNumber);
                    }
                }
                else
                {
                    return false;
                }
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 读取设备属性描述
        /// </summary>
        /// <param name="dbNumber">数据库编号</param>
        /// <param name="itemLabel">标签名称</param>
        /// <param name="itemValue">返回属性描述</param>
        /// <returns></returns>
        public bool ReadDiaplayName(string dbNumber, string itemLabel, ref string itemValue)
        {
            try
            {
                string sendCmd = "ns=" + dbNumber + ";s=" + itemLabel;
                sendCmd = sendCmd.Replace(".PV", ".ValueDesp");
                Opc.Ua.DataValue value = opcUaClient.ReadNode(sendCmd);
                itemValue = SetDataValueToString(value);
                return true;
            }
            catch (Exception)
            {
                itemValue = "";
                return false;
            }
        }

        /// <summary>
        /// 读取设备属性描述
        /// </summary>
        /// <param name="sendCmd">读取属性描述命令</param>
        /// <param name="itemValue">返回属性描述</param>
        /// <returns></returns>
        public bool ReadDiaplayName(string sendCmd, ref string itemValue)
        {
            try
            {
                Opc.Ua.DataValue value = opcUaClient.ReadNode(sendCmd);
                itemValue = SetDataValueToString(value);
                return true;
            }
            catch (Exception)
            {
                itemValue = "";
                return false;
            }
        }

        /// <summary>
        /// 写入标签值，如果标签不存在则返回false
        /// </summary>
        /// <param name="dbNumber">OBJ或FEP在server中的数据库编号</param>
        /// <param name="itemLabel">标签名称</param>
        /// <param name="itemValue">写入值</param>
        /// <returns></returns>
        public bool WriteItemValue(string dbNumber, string itemLabel, object itemValue)
        {
            try
            {
                string sendCmd = "ns=" + dbNumber + ";s=" + itemLabel;
                bool IsSuccess = opcUaClient.WriteNode(sendCmd, itemValue);
                return IsSuccess;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 写入标签值，如果标签不存在则返回false
        /// </summary>
        /// <param name="sendCmd">写入值命令</param>
        /// <param name="itemValue">写入值</param>
        /// <returns></returns>
        public bool WriteItemValue(string[] sendCmd, object[] itemValue)
        {
            try
            {
                bool IsSuccess = opcUaClient.WriteNodes(sendCmd, itemValue);
                return IsSuccess;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 将未知类型的值转换为string
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private string SetDataValueToString(Opc.Ua.DataValue value)
        {
            string tempValue = "";
            // 一个数据的类型是不是数组由 value.WrappedValue.TypeInfo.ValueRank 来决定的
            // -1 说明是一个数值
            // 1  说明是一维数组，如果类型BuiltInType是Int32，那么实际是int[]
            // 2  说明是二维数组，如果类型BuiltInType是Int32，那么实际是int[,]
            if (value == null)
            {
                return "";
            }
            switch (value.WrappedValue.TypeInfo.BuiltInType)
            {
                case Opc.Ua.BuiltInType.Int32:
                    {
                        if (value.WrappedValue.TypeInfo.ValueRank == -1)
                        {
                            int temp = (int)value.WrappedValue.Value;
                            tempValue = temp.ToString();
                        }
                        else if (value.WrappedValue.TypeInfo.ValueRank == 1)
                        {
                            int[] temp = (int[])value.WrappedValue.Value;
                            foreach (int singleValue in temp)
                            {
                                tempValue += singleValue.ToString() + ",";
                            }
                        }
                        else if (value.WrappedValue.TypeInfo.ValueRank == 2)
                        {
                            int[,] temp = (int[,])value.WrappedValue.Value;
                            tempValue = temp[0, 0].ToString();
                            foreach (int singleValue in temp)
                            {
                                tempValue += singleValue.ToString() + ",";
                            }
                        }
                    }
                    break;
                case Opc.Ua.BuiltInType.Int16:
                    {
                        Int16 temp = (Int16)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.UInt16:
                    {
                        UInt16 temp = (UInt16)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.Int64:
                    {
                        Int64 temp = (Int64)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.UInt64:
                    {
                        UInt64 temp = (UInt64)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.UInt32:
                    {
                        UInt32 temp = (UInt32)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.Float:
                    {
                        float temp = (float)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.String:
                    {
                        string temp = (string)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.DateTime:
                    {
                        DateTime temp = (DateTime)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.LocalizedText:
                    {
                        LocalizedText temp = (LocalizedText)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.Byte:
                    {
                        Byte temp = (Byte)value.WrappedValue.Value;
                        tempValue = temp.ToString();
                    }
                    break;
                case Opc.Ua.BuiltInType.Boolean:
                    {
                        Boolean temp = (Boolean)value.WrappedValue.Value;
                        if (temp == true)
                        {
                            tempValue = "1";
                        }
                        else
                        {
                            tempValue = "0";
                        }
                    }
                    break;
                default:
                    break;
            }
            return tempValue;
        }

    }
}
